using System.Reflection;
using Castle.DynamicProxy;
using KL.Core.Common.Caches;
using KL.Standard.Model.Infrastructure;
using KL.Standard.Model.Attribute;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace KL.Core.Extensions.AOP;

/// <summary>
/// 用于缓存的拦截器
/// </summary>
public class CacheInterceptor : IInterceptor
{
    /// <summary>
    /// 缓存接口
    /// </summary>
    private readonly ICaching _caching;

    /// <summary>
    /// 日志
    /// </summary>
    private readonly ILogger<CacheInterceptor> _logger;
    
    public CacheInterceptor(ILogger<CacheInterceptor> logger, ICaching caching)
    {
        _logger = logger;
        _caching = caching;
    }

    public void Intercept(IInvocation invocation)
    {
        // var appCustomOptions = Options.Value;
        // if (!appCustomOptions.openRedisCache)
        // {
        //     invocation.Proceed();
        //     return;
        // }

        MethodInfo methodInfo = invocation.MethodInvocationTarget;

        if (methodInfo == null)
            methodInfo = invocation.Method;
        // 如果为以下类型则无需读取缓存
        var methodReturnType = methodInfo.ReturnType;
        if (methodReturnType == typeof(void) || methodReturnType == typeof(Task) ||
            methodReturnType == typeof(ValueTask))
        {
            invocation.Proceed();
            return;
        }

        #region 判断是否有打上RedisCache的标签

        CacheAttribute cacheAttribute = methodInfo.GetCustomAttributes<CacheAttribute>(true).FirstOrDefault();
        if (cacheAttribute != null)
        {
            #region GetAndSave Cache

            if (cacheAttribute.opeType.Equals("GetAndSave"))
            {
                #region 生成cacheKey

                var cacheKey = string.Empty;
                if (string.IsNullOrEmpty(cacheAttribute.cacheKey))
                {
                    cacheKey = $"{cacheAttribute.keyPrefix}_{methodInfo.Name}";

                    #region 参数变成字符串

                    if (!cacheAttribute.ignoreParam)
                    {
                        var para1 = invocation.Arguments.Where(i => i.GetType().Equals(cacheAttribute.paramType))
                            .FirstOrDefault();
                        if (para1 != null)
                        {
                            cacheKey += "~" + para1.ToString();
                        }
                    }

                    #endregion
                }
                else
                {
                    cacheKey = cacheAttribute.cacheKey;
                }

                #endregion

                var cacheValue = _caching.Get<string>(cacheKey);
                if (cacheValue != null)
                {
                    #region 将缓存数据转换为输出的对象

                    //将当前获取到的缓存值，赋值给当前执行方法
                    var type = invocation.Method.ReturnType;
                    var resultTypes = type.GenericTypeArguments;

                    object response;
                    if (type != null && typeof(Task).IsAssignableFrom(type))
                    {
                        //核心2：返回异步对象Task<T>
                        if (resultTypes.Any())
                        {
                            var resultType = resultTypes.FirstOrDefault();
                            // 核心3，直接序列化成 dynamic 类型，之前我一直纠结特定的实体
                            dynamic temp = JsonConvert.DeserializeObject(cacheValue, resultType);
                            response = typeof(Task).GetMethod(nameof(Task.FromResult))
                                ?.MakeGenericMethod(resultType)
                                .Invoke(null, new[] { temp });

                        }
                        else
                        {
                            //Task 无返回方法 指定时间内不允许重新运行
                            response = Task.Yield();
                        }
                    }
                    else
                    {
                        // 核心4，要进行 ChangeType
                        response = JsonConvert.DeserializeObject(cacheValue, type);
                        //response = System.Convert.ChangeType(cacheValue, type);
                    }

                    #endregion

                    //去执行当前的方法
                    invocation.ReturnValue = response;
                    _logger.LogInformation($"读取缓存（key={cacheKey}）成功");
                    return;

                }

                invocation.Proceed();

                #region 存入缓存

                if (!string.IsNullOrWhiteSpace(cacheKey))
                {
                    object response;

                    //Type type = invocation.ReturnValue?.GetType();
                    var type = invocation.Method.ReturnType;
                    if (type != null && typeof(Task).IsAssignableFrom(type))
                    {
                        var resultProperty = type.GetProperty("Result");
                        var returnTask = (Task)invocation.ReturnValue;
                        returnTask.Wait();
                        response = resultProperty.GetValue(invocation.ReturnValue);
                    }
                    else
                    {
                        response = invocation.ReturnValue;
                    }

                    if (response == null) response = string.Empty;
                    TimeSpan? expire =cacheAttribute.cacheExpireSecd!=-1?  TimeSpan.FromSeconds(cacheAttribute.cacheExpireSecd):null;
                    // 核心5：将获取到指定的response 和特性的缓存时间，进行set操作
                    _caching.Set(cacheKey, JsonConvert.SerializeObject(response), expire);
                    _logger.LogInformation($"写入缓存（key={cacheKey}）成功");
                }

                #endregion
            }

            #endregion

            #region remove cache

            else if (cacheAttribute.opeType.Equals("remove"))
            {
                invocation.Proceed();
                if (!string.IsNullOrEmpty(cacheAttribute.cacheKey))
                {
                    var returnValue = invocation.ReturnValue;
                    var removeFlag = false;
                    if (returnValue is BaseResult<string>)
                    {
                        var result = returnValue as BaseResult<string>;
                        if (result != null && result.success)
                            removeFlag = true;
                    }

                    if (removeFlag)
                    {
                        _caching.Remove(cacheAttribute.cacheKey);
                        _logger.LogInformation($"删除缓存（key={cacheAttribute.cacheKey}）成功");
                    }

                }
            }

            #endregion

            else
            {
                invocation.Proceed();
            }

        }
        else
        {
            //如果没有标记[RedisCache]，直接执行方法
            invocation.Proceed();
        }

        #endregion
    }
}